// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ControllerStateMachineDescription.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

// This file provides shared controller state machine enums and structures between  C# and C++ libraries.
// Symbol CSHARP is #defined for compiling as C#
// To include in cpp do:
//  #include "ControllerStateMachineExports.h"
//  #include <ControllerStateMachineDescription.cs>

#if !Kuka_ControllerStateMachineDescription_defined
// Cannot use #pragma once in C# / cpp shared header file
#define Kuka_ControllerStateMachineDescription_defined

#if CSHARP
namespace Microsoft.Robotics.Manipulation.ControllerStateMachine
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;

    /// <summary>
    /// Controller status enumeration
    /// </summary>
    public enum ControllerStatus
#else
#pragma once
    // The following structures are intended to be serialized, so ensure that they
    // are packed so they 
#pragma pack(push, 1)
    enum CONTROLLERSTATEMACHINEEXPORTS_DLLEXPORTIMPORT ControllerStatus
#endif
    {
        /// <summary> Controller is not started. </summary>
        NotStarted,

        /// <summary> Controller has been started and is currently running. </summary>
        Running,

        /// <summary> 
        /// Controller has been stopped. 
        /// </summary>
        Stopped,

        /// <summary> 
        /// Controller has stopped due to an error condition. 
        /// </summary>
        Error
    }

    /// <summary>
    /// Defines the possible types of registers
    /// </summary>
#if CSHARP
    public enum
#else
;
enum CONTROLLERSTATEMACHINEEXPORTS_DLLEXPORTIMPORT
#endif

 RegisterTypes
    {
        /// <summary>
        /// Invalid -- used when register type is unspecified
        /// </summary>
        Invalid,
 
        /// <summary>
        /// Working registers
        /// </summary>
        Working,

        /// <summary>
        /// Feedback registers
        /// </summary>
        Feedback
   }

    /// <summary> 
    /// Indicates if CSMD/Working buffers are ready to be read in control thread.
    /// </summary>
#if CSHARP
    public enum
#else
;
enum CONTROLLERSTATEMACHINEEXPORTS_DLLEXPORTIMPORT
#endif

 CsmdReadyStates
    {
        /// <summary>
        /// not ready (buffers should not current be consumed by controller thread)
        /// </summary>
        NotReady,

        /// <summary>
        /// ready (consistent state machine and registers present in exchange buffers)
        /// </summary>
        Ready,

        /// <summary>
        /// consuming (controller thread is in process of consuming new state machine and registers, so message handler thread should not change them until status changes back to "not ready")
        /// </summary>
        Consuming
    }

#if CSHARP
#else
    ;
#endif

    /// <summary>
    /// Defines an operation including result and operand locations.
    /// </summary>
#if CSHARP
    [DataContract]
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct

#else
        ;
        struct CONTROLLERSTATEMACHINEEXPORTS_DLLEXPORTIMPORT 
#endif
 RegisterID
    {
        /// <summary> Type of register (e.g. working or feedback) </summary>
#if CSHARP
        public
#endif
 RegisterTypes RegisterType;

        /// <summary> Register number </summary>
#if CSHARP
        public
#endif
 int RegisterNumber;
        
#if CSHARP
        /// <summary>
        /// Initializes a new instance of the <see cref="RegisterID" /> struct.
        /// </summary>
        /// <param name="registerType">the register type</param>
        /// <param name="registerNumber">the register number</param>
        public RegisterID(RegisterTypes registerType, int registerNumber)
        {
            this.RegisterType = registerType;
            this.RegisterNumber = registerNumber;
        }

        /// <summary>
        /// Generates a RegisterID for working register.
        /// </summary>
        /// <param name="registerNumber">The register number.</param>
        /// <returns>Corresponding RegisterID.</returns>
        public static RegisterID NewWorkingRegister(int registerNumber)
        {
            return new RegisterID(
                RegisterTypes.Working,
                registerNumber);
        }

        /// <summary>
        /// Generates a RegisterID for a feedback register.
        /// </summary>
        /// <param name="registerNumber">The register number.</param>
        /// <returns>Corresponding RegisterID></returns>
        public static RegisterID NewFeedbackRegister(int registerNumber)
        {
            return new RegisterID(
                RegisterTypes.Feedback,
                registerNumber);
        }
#else        
        RegisterID();
#endif
    }

    /// <summary>
    /// Defines an operation including result and operand locations.
    /// </summary>
#if CSHARP
    [DataContract]
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct ControllerOperation
#else
        ;
        struct CONTROLLERSTATEMACHINEEXPORTS_DLLEXPORTIMPORT ControllerOperation
#endif
    {
        /// <summary> Operation op-code </summary>
#if CSHARP
        public
#endif
 int OpCode;

        /// <summary> Register for result.  Assumed to always be present. </summary>
#if CSHARP
        public
#endif
 RegisterID Result;

        /// <summary> Number of operands </summary>
#if CSHARP
        public
#endif
 int NumOperands;

        /// <summary> Register for operand 0 </summary>
#if CSHARP
        public
#endif
 RegisterID Operand0;

        /// <summary> Register for operand 1 </summary>
#if CSHARP
        public
#endif
 RegisterID Operand1;

        /// <summary> Register for operand 2 </summary>
#if CSHARP
        public
#endif
 RegisterID Operand2;

        /// <summary> Register for operand 3 </summary>
#if CSHARP
        public
#endif
 RegisterID Operand3;

        /// <summary> Register for operand 4 </summary>
#if CSHARP
        public
#endif
 RegisterID Operand4;

        /// <summary> Register for operand 5 </summary>
#if CSHARP
        public
#endif
 RegisterID Operand5;

#if CSHARP
        /// <summary>
        /// Initializes a new instance of the <see cref="ControllerOperation"/> struct
        /// </summary>
        /// <param name="opCode">Op code.</param>
        /// <param name="result">Result register</param>
        /// <param name="operands">Operand registers</param>
        public ControllerOperation(int opCode, RegisterID result, RegisterID[] operands)
        {
            this.OpCode = opCode;
            this.Result = result;

            if (operands == null)
            {
                this.NumOperands = 0;
            }
            else
            {
                if (operands.Length > ControllerStateMachineDescriptionConstants.MaxOperandsPerOperation)
                {
                    throw new ArgumentException("Maximum of " +
                        ControllerStateMachineDescriptionConstants.MaxOperandsPerOperation +
                        " operands supported for a single operation");
                }

                this.NumOperands = operands.Length;
            }

            if (this.NumOperands > 0)
            {
                this.Operand0 = operands[0];
            }
            else
            {
                this.Operand0 = new RegisterID();
            }

            if (this.NumOperands > 1)
            {
                this.Operand1 = operands[1];
            }
            else
            {
                this.Operand1 = new RegisterID();
            }

            if (this.NumOperands > 2)
            {
                this.Operand2 = operands[2];
            }
            else
            {
                this.Operand2 = new RegisterID();
            }

            if (this.NumOperands > 3)
            {
                this.Operand3 = operands[3];
            }
            else
            {
                this.Operand3 = new RegisterID();
            }

            if (this.NumOperands > 4)
            {
                this.Operand4 = operands[4];
            }
            else
            {
                this.Operand4 = new RegisterID();
            }

            if (this.NumOperands > 5)
            {
                this.Operand5 = operands[5];
            }
            else
            {
                this.Operand5 = new RegisterID();
            }
        }
#endif

        /// <summary> Initializes a new instance of the <c>ControllerOperation</c> struct. </summary>
#if !CSHARP
      ControllerOperation();
#endif
    }

    /// <summary>
    /// Defines a conditional state transition and operation to occur on transition.
    /// </summary>
#if CSHARP
    [DataContract]
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct OperationList
#else
    ;
    struct CONTROLLERSTATEMACHINEEXPORTS_DLLEXPORTIMPORT OperationList
#endif
    {
        /// <summary> Number of operations in the list. </summary>
#if CSHARP
        public
#endif
        int NumOperations;

        /// <summary> First operation to perform. </summary>
#if CSHARP
        public
#endif
 ControllerOperation Operation0;

        /// <summary> Second operation to perform. </summary>
#if CSHARP
        public
#endif
 ControllerOperation Operation1;

        /// <summary> Third operation to perform. </summary>
#if CSHARP
        public
#endif
 ControllerOperation Operation2;

#if CSHARP
        /// <summary>
        /// Initializes a new instance of the <see cref="OperationList"/> struct.
        /// </summary>
        /// <param name="operations">Array of operations to perform.</param>
        public OperationList(ControllerOperation[] operations)
        {
            if (operations == null)
            {
                this.NumOperations = 0;
            }
            else
            {
                if (operations.Length > ControllerStateMachineDescriptionConstants.MaxOperationsPerState)
                {
                    throw new ArgumentException("Maximum of " +
                        ControllerStateMachineDescriptionConstants.MaxOperationsPerState +
                        " operations allowed");
                }

                this.NumOperations = operations.Length;
            }

            if (this.NumOperations > 0)
            {
                this.Operation0 = operations[0];
            }
            else
            {
                this.Operation0 = new ControllerOperation();
            }

            if (this.NumOperations > 1)
            {
                this.Operation1 = operations[1];
            }
            else
            {
                this.Operation1 = new ControllerOperation();
            }
 
            if (this.NumOperations > 2)
            {
                this.Operation2 = operations[2];
            }
            else
            {
                this.Operation2 = new ControllerOperation();
            }
        }
#endif

        /// <summary> Initializes a new instance of the <c>OperationList</c> struct </summary>
#if !CSHARP
	OperationList();
#endif
    }

    /// <summary>
    /// Defines a conditional state transition and operation to occur on transition.
    /// </summary>
#if CSHARP
    [DataContract]
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct ControllerTransition
#else
;
struct CONTROLLERSTATEMACHINEEXPORTS_DLLEXPORTIMPORT ControllerTransition
#endif
    {
        /// <summary> State to transition to </summary>
#if CSHARP
        public
#endif
 int TargetState;

        /// <summary> Test for transition condition.  Must return a single integer, where 0 indicates false.</summary>
#if CSHARP
        public
#endif
 int TestOpCode;

        /// <summary> Number of operands to the test operation </summary>
#if CSHARP
        public
#endif
 int NumTestOperands;

        /// <summary> First operand to test operation. </summary>
#if CSHARP
        public
#endif
 RegisterID TestOperand0;

        /// <summary> Second operand to test operation. </summary>
#if CSHARP
        public
#endif
 RegisterID TestOperand1;

        /// <summary> Third operand to test operation. </summary>
#if CSHARP
        public
#endif
 RegisterID TestOperand2;

        /// <summary> Operations to perform on transition. </summary>
#if CSHARP
        public
#endif
 OperationList Operations;

#if CSHARP
        /// <summary>
        /// Initializes a new instance of the <see cref="ControllerTransition"/> struct.
        /// </summary>
        /// <param name="targetState">Target state.</param>
        /// <param name="testOpCode">Op code for testing whether transition triggers.</param>
        /// <param name="testOperands">IDs for registers to draw test operation operands from.</param>
        /// <param name="operations">Operations to perform when transition triggers.</param>
        public ControllerTransition(
            int targetState,
            int testOpCode,
            RegisterID[] testOperands,
            OperationList operations)
        {
            if (testOperands == null)
            {
                this.NumTestOperands = 0;
            }
            else
            {
                if (testOperands.Length > ControllerStateMachineDescriptionConstants.MaxTestOperands)
                {
                    throw new ArgumentException("Maximum of " +
                        ControllerStateMachineDescriptionConstants.MaxTestOperands +
                        " test operands allowed");
                }

                this.NumTestOperands = testOperands.Length;
            }

            this.TargetState = targetState;
            this.TestOpCode = testOpCode;

            this.Operations = operations;

            if (this.NumTestOperands > 0)
            {
                this.TestOperand0 = testOperands[0];
            }
            else
            {
                this.TestOperand0 = new RegisterID();
            }

            if (this.NumTestOperands > 1)
            {
                this.TestOperand1 = testOperands[1];
            }
            else
            {
                this.TestOperand1 = new RegisterID();
            }

            if (this.NumTestOperands > 2)
            {
                this.TestOperand2 = testOperands[2];
            }
            else
            {
                this.TestOperand2 = new RegisterID();
            }
        }

#endif

        /// <summary> Initializes a new instance of the <c>ControllerTransition</c> struct </summary>
#if !CSHARP
	ControllerTransition();
#endif
    }

    /// <summary>
    /// Defines a state including control mode, operations, and transitions.
    /// </summary>
#if CSHARP
    [DataContract]
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct ControllerState
#else
;
    struct CONTROLLERSTATEMACHINEEXPORTS_DLLEXPORTIMPORT ControllerState
#endif
    {
        /// <summary> Control mode. </summary>
#if CSHARP
        public
#endif
 int ControlMode;

        /// <summary> State ID in the execution stet machine </summary>
#if CSHARP
        public
#endif
 int StateID;

        /// <summary> Number of transitions. </summary>
#if CSHARP
        public
#endif
 int NumTransitions;

        /// <summary> Operations to perform on transition. </summary>
#if CSHARP
        public
#endif
 OperationList Operations;

        /// <summary> The first transition. </summary>
#if CSHARP
        public
#endif
 ControllerTransition Transition0;

        /// <summary> The second transition. </summary>
#if CSHARP
        public
#endif
 ControllerTransition Transition1;

        /// <summary> The third transition. </summary>
#if CSHARP
        public
#endif
 ControllerTransition Transition2;

#if CSHARP
        /// <summary>
        /// Initializes a new instance of the <see cref="ControllerState"/> struct.
        /// </summary>
        /// <param name="controlMode">Control mode.</param>
        /// <param name="stateID">ID of state.</param>
        /// <param name="operations">Operations performed in the state.</param>
        /// <param name="transitions">Transitions away from the state.</param>
        public ControllerState(
            int controlMode,
            int stateID,
            OperationList operations,
            ControllerTransition[] transitions)
        {
            if (transitions == null)
            {
                this.NumTransitions = 0;
            }
            else
            {
                if (transitions.Length > ControllerStateMachineDescriptionConstants.MaxNumberOfTransitions)
                {
                    throw new ArgumentException("Maximum of " +
                        ControllerStateMachineDescriptionConstants.MaxNumberOfTransitions +
                        " transitions allowed");
                }

                this.NumTransitions = transitions.Length;
            }

            this.ControlMode = controlMode;
            this.StateID = stateID;
            this.Operations = operations;

            if (this.NumTransitions > 0)
            {
                this.Transition0 = transitions[0];
            }
            else
            {
                this.Transition0 = new ControllerTransition();
            }

            if (this.NumTransitions > 1)
            {
                this.Transition1 = transitions[1];
            }
            else
            {
                this.Transition1 = new ControllerTransition();
            }

            if (this.NumTransitions > 2)
            {
                this.Transition2 = transitions[2];
            }
            else
            {
                this.Transition2 = new ControllerTransition();
            }
        }

#endif

        /// <summary> Initializes a new instance of the <c>ControllerState</c> struct. </summary>
#if !CSHARP
	ControllerState();
#endif
    }

    /// <summary>
    /// Defines a controller.
    /// </summary>
#if CSHARP
    [DataContract]
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct ControllerStateMachineDescription
#else
    ;
    struct CONTROLLERSTATEMACHINEEXPORTS_DLLEXPORTIMPORT ControllerStateMachineDescription
#endif
    {
        /// <summary> Number of states </summary>
#if CSHARP
        public
#endif
 int NumStates;

        /// <summary> The first state.  Controller always starts in this state. </summary>
#if CSHARP
        public
#endif
 ControllerState State0;

        /// <summary> The second state. </summary>
#if CSHARP
        public
#endif
 ControllerState State1;

        /// <summary> The third state. </summary>
#if CSHARP
        public
#endif
 ControllerState State2;

#if CSHARP
        /// <summary>
        /// Initializes a new instance of the <see cref="ControllerStateMachineDescription"/> struct.
        /// </summary>
        /// <param name="states">States comprising the state machine.</param>
        public ControllerStateMachineDescription(ControllerState[] states)
        {
            if (!(states.Length > 0))
            {
                throw new ArgumentException("A state machine cannot have 0 states");
            }

            if (states.Length > ControllerStateMachineDescriptionConstants.MaxNumberOfStates)
            {
                throw new ArgumentException("A maximum of " +
                    ControllerStateMachineDescriptionConstants.MaxNumberOfStates +
                    " states are permitted");
            }

            this.NumStates = states.Length;

            this.State0 = states[0];

            if (states.Length > 1)
            {
                this.State1 = states[1];
            }
            else
            {
                this.State1 = new ControllerState();
            }

            if (states.Length > 2)
            {
                this.State2 = states[2];
            }
            else
            {
                this.State2 = new ControllerState();
            }
        }

#endif

        /// <summary> Initializes a new instance of the <c>ControllerStateMachineDescription</c> struct </summary>
#if !CSHARP
	ControllerStateMachineDescription();
#endif
    }

#if CSHARP
    /// <summary>
    /// Constants defining the size/form of the controller state machine description
    /// </summary>
    public class ControllerStateMachineDescriptionConstants
    {
        /// <summary>
        /// Maximum number of operands per operation.
        /// </summary>
        public const int MaxOperandsPerOperation = 6;

        /// <summary>
        /// Maximum number of operations per state.
        /// </summary>
        public const int MaxOperationsPerState = 3;

        /// <summary>
        /// Maximum number of operations per transition
        /// </summary>
        public const int MaxOperationsPerTransition = MaxOperationsPerState;

        /// <summary>
        /// Maximum number of operands to a transition test operation.
        /// </summary>
        public const int MaxTestOperands = 3;

        /// <summary>
        /// Maximum number of transitions per state.
        /// </summary>
        public const int MaxNumberOfTransitions = 3;

        /// <summary>
        /// Maximum number of states.
        /// </summary>
        public const int MaxNumberOfStates = 3;
    }

#endif

#if CSHARP
}
#else
;
#pragma pack(pop)
#endif

#endif
